To get around the fact that we may have a owner_events = FALSE grab in
authorOwen Taylor <otaylor@redhat.com>
Sat, 2 Feb 2002 21:50:46 +0000 (21:50 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Sat, 2 Feb 2002 21:50:46 +0000 (21:50 +0000)
Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>

        * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
        that we may have a owner_events = FALSE grab in effect
        when we pop up a window, make a temporary grab on a
        different window, then grab on the real window. Fixes
        a problem where if a context menu popped up under the
        cursor, the first item would be stuck unselected.
        (#59812, reported by Arnaud Charlet.)

ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkmenu.c

index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index 99afa47910022ea9fe2dcb297e62f1ab417f4d43..7031df09152721a9459b05715c340d2ce9fbc5e1 100644 (file)
@@ -1,3 +1,13 @@
+Sat Feb  2 16:43:31 2002  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkmenu.c (gtk_menu_popup): To get around the fact
+       that we may have a owner_events = FALSE grab in effect
+       when we pop up a window, make a temporary grab on a
+       different window, then grab on the real window. Fixes
+       a problem where if a context menu popped up under the 
+       cursor, the first item would be stuck unselected.
+       (#59812, reported by Arnaud Charlet.)
+
 2002-02-02  Peter Williams  <peterw@ximian.com>
 
        * gtk/Makefile.am (install-data-local): Install gtkrc.default
index e0d69a4aae503423bf37f1d18475d11683032f59..ede02af4bdc43d0c34814c68d6606c927a8c11c8 100644 (file)
@@ -136,6 +136,9 @@ static void gtk_menu_remove         (GtkContainer      *menu,
 
 static void gtk_menu_update_title   (GtkMenu           *menu);
 
+static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
+static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);
+
 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
                                           gboolean group_changed);
 
@@ -594,6 +597,29 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
     }
 }
 
+static gboolean
+popup_grab_on_window (GdkWindow *window,
+                     guint32    activate_time)
+{
+  if ((gdk_pointer_grab (window, TRUE,
+                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                        GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+                        GDK_POINTER_MOTION_MASK,
+                        NULL, NULL, activate_time) == 0))
+    {
+      if (gdk_keyboard_grab (window, TRUE,
+                            activate_time) == 0)
+       return TRUE;
+      else
+       {
+         gdk_pointer_ungrab (activate_time);
+         return FALSE;
+       }
+    }
+
+  return FALSE;
+}
+
 void
 gtk_menu_popup (GtkMenu                    *menu,
                GtkWidget           *parent_menu_shell,
@@ -615,6 +641,72 @@ gtk_menu_popup (GtkMenu                *menu,
   menu_shell = GTK_MENU_SHELL (menu);
   
   menu_shell->parent_menu_shell = parent_menu_shell;
+
+  /* Find the last viewable ancestor, and make an X grab on it
+   */
+  parent = GTK_WIDGET (menu);
+  xgrab_shell = NULL;
+  while (parent)
+    {
+      gboolean viewable = TRUE;
+      GtkWidget *tmp = parent;
+      
+      while (tmp)
+       {
+         if (!GTK_WIDGET_MAPPED (tmp))
+           {
+             viewable = FALSE;
+             break;
+           }
+         tmp = tmp->parent;
+       }
+      
+      if (viewable)
+       xgrab_shell = parent;
+      
+      parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
+    }
+
+  /* We want to receive events generated when we map the menu; unfortunately,
+   * since there is probably already an implicit grab in place from the
+   * button that the user used to pop up the menu, we won't receive then --
+   * in particular, the EnterNotify when the menu pops up under the pointer.
+   *
+   * If we are grabbing on a parent menu shell, no problem; just grab on
+   * that menu shell first before popping up the window with owner_events = TRUE.
+   *
+   * When grabbing on the menu itself, things get more convuluted - we
+   * we do an explicit grab on a specially created window with
+   * owner_events = TRUE, which we override further down with a grab
+   * on the menu. (We can't grab on the menu until it is mapped; we
+   * probably could just leave the grab on the other window, with a
+   * little reorganization of the code in gtkmenu*).
+   */
+  if (xgrab_shell == widget)
+    {
+      if (popup_grab_on_window (xgrab_shell->window, activate_time))
+       GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+    }
+  else
+    {
+      GdkWindow *transfer_window;
+
+      xgrab_shell = widget;
+      transfer_window = menu_grab_transfer_window_get (menu);
+      if (popup_grab_on_window (transfer_window, activate_time))
+       GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+    }
+
+  if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
+    {
+      /* We failed to make our pointer/keyboard grab. Rather than leaving the user
+       * with a stuck up window, we just abort here. Presumably the user will
+       * try again.
+       */
+      menu_shell->parent_menu_shell = NULL;
+      return;
+    }
+
   menu_shell->active = TRUE;
   menu_shell->button = button;
 
@@ -654,46 +746,8 @@ gtk_menu_popup (GtkMenu                *menu,
 
   gtk_menu_scroll_to (menu, menu->scroll_offset);
 
-  /* Find the last viewable ancestor, and make an X grab on it
-   */
-  parent = GTK_WIDGET (menu);
-  xgrab_shell = NULL;
-  while (parent)
-    {
-      gboolean viewable = TRUE;
-      GtkWidget *tmp = parent;
-      
-      while (tmp)
-       {
-         if (!GTK_WIDGET_MAPPED (tmp))
-           {
-             viewable = FALSE;
-             break;
-           }
-         tmp = tmp->parent;
-       }
-      
-      if (viewable)
-       xgrab_shell = parent;
-      
-      parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
-    }
-  
-  if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
-    {
-      if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
-                            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-                            GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
-                            GDK_POINTER_MOTION_MASK,
-                            NULL, NULL, activate_time) == 0))
-       {
-         if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
-                                 activate_time) == 0)
-           GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
-         else
-           gdk_pointer_ungrab (activate_time);
-       }
-    }
+  if (xgrab_shell == widget)
+    popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
 
   gtk_grab_add (GTK_WIDGET (menu));
 }
@@ -766,6 +820,8 @@ gtk_menu_popdown (GtkMenu *menu)
 
   menu_shell->have_xgrab = FALSE;
   gtk_grab_remove (GTK_WIDGET (menu));
+
+  menu_grab_transfer_window_destroy (menu);
 }
 
 GtkWidget*
@@ -1241,6 +1297,51 @@ gtk_menu_realize (GtkWidget *widget)
   gdk_window_show (menu->view_window);
 }
 
+/* See notes in gtk_menu_popup() for information about the "grab transfer window"
+ */
+static GdkWindow *
+menu_grab_transfer_window_get (GtkMenu *menu)
+{
+  GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
+  if (!window)
+    {
+      GdkWindowAttr attributes;
+      gint attributes_mask;
+      
+      attributes.x = -100;
+      attributes.y = -100;
+      attributes.width = 10;
+      attributes.height = 10;
+      attributes.window_type = GDK_WINDOW_TEMP;
+      attributes.wclass = GDK_INPUT_ONLY;
+      attributes.override_redirect = TRUE;
+      attributes.event_mask = 0;
+
+      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
+      
+      window = gdk_window_new (NULL, &attributes, attributes_mask);
+      gdk_window_set_user_data (window, menu);
+
+      gdk_window_show (window);
+
+      g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", window);
+    }
+
+  return window;
+}
+
+static void
+menu_grab_transfer_window_destroy (GtkMenu *menu)
+{
+  GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
+  if (window)
+    {
+      gdk_window_set_user_data (window, NULL);
+      gdk_window_destroy (window);
+      g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", NULL);
+    }
+}
+
 static void
 gtk_menu_unrealize (GtkWidget *widget)
 {
@@ -1250,6 +1351,8 @@ gtk_menu_unrealize (GtkWidget *widget)
 
   menu = GTK_MENU (widget);
 
+  menu_grab_transfer_window_destroy (menu);
+
   gdk_window_set_user_data (menu->view_window, NULL);
   gdk_window_destroy (menu->view_window);
   menu->view_window = NULL;